﻿using System;
using System.Configuration;
using System.Xml;

namespace Atom.Common.CustomConfiguration
{
    public class NameTypeElementCollection<T> : ConfigurationElementCollection where T : NameTypeConfigurationElement
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return Activator.CreateInstance<T>();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return (element as NameTypeConfigurationElement).Name;
        }

        protected virtual Type RetrieveConfigurationElementType(XmlReader reader)
        {
            Type configurationElementType = null;
            if (reader.AttributeCount > 0)
            {
                for (bool go = reader.MoveToFirstAttribute(); go; go = reader.MoveToNextAttribute())
                {
                    if ("type".Equals(reader.Name))
                    {
                        Type providerType = Type.GetType(reader.Value, false);
                        Attribute attribute = Attribute.GetCustomAttribute(providerType, typeof(ConfigurationElementTypeAttribute));
                        if (attribute == null)
                        {
                            throw new ConfigurationErrorsException("No ConfigurationElementTypeAttribute is applied.");
                        }
                        configurationElementType = ((ConfigurationElementTypeAttribute)attribute).ConfigurationElementType;
                        break;
                    }
                }
                reader.MoveToElement();
            }
            return configurationElementType;
        }

        protected override bool OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)
        {
            if (base.AddElementName.Equals(elementName))
            {
                Type configurationElementType = this.RetrieveConfigurationElementType(reader);
                var currentElement = (T)Activator.CreateInstance(configurationElementType);
                currentElement.DeserializeElement(reader);
                base.BaseAdd(currentElement, true);
                return true;
            }
            return base.OnDeserializeUnrecognizedElement(elementName, reader);
        }

        public T GetConfigurationElement(string name)
        {
            return  (T)this.BaseGet(name);           
        }
    }
}